#pragma once 

#include "db.hpp"
#include "logger.hpp"
#include "matcher.hpp"
#include "online.hpp"
#include "room.hpp"
#include "session.hpp"
#include "util.hpp"
#include "logger.hpp"

#define WWWROOT "./webroot/"

class gobang_server{
private:
    wsserver_t _wssrv;
    std::string _web_root;////静态资源根⽬录 ./wwwroot/ /register.html ->./wwwroot/register.html
    user_table _ut;
    online_manager _om;
    room_manager _rm;
    matcher _mm;
    session_manager _sm;
private:
    void file_handler(wsserver_t::connection_ptr& conn){
        //静态资源请求的处理
        //1. 获取到请求uri-资源路径，了解客⼾端请求的⻚⾯⽂件名称
        websocketpp::http::parser::request req=conn->get_request();
        std::string uri=req.get_uri();
        //2组合出文件的实际路径  相对根目录+uri
        std::string realpath=_web_root+uri;
        //3.如果访问的是目录，增加一个后缀 login.html ,/login.html
        if(realpath.back()=='/'){
            realpath+="login.html";
        }
        //4.打开对应文件，并返回给客户端，如果没有这个文件则返回404
        std::string body;
        bool ret=file_util::read(realpath,body);
        if(ret==false){
            body += "<html>";
            body += "<head>";
            body += "<meta charset='UTF-8'/>";
            body += "</head>";
            body += "<body>";
            body += "<h1> Not Found 404 </h1>";
            body += "</body>";
            //设置响应正文
            conn->set_body(body);
            conn->set_status(websocketpp::http::status_code::not_found);
            return;
        }
        conn->set_body(body);
        conn->set_status(websocketpp::http::status_code::ok);
    }
    void http_resp(wsserver_t::connection_ptr&conn ,bool result,
    websocketpp::http::status_code::value code,const std::string&reason){
        Json::Value json_resp;
        json_resp["result"]=result;
        json_resp["reason"]=reason;
        std::string resp_body;
        json_util::serialize(json_resp,resp_body);
        conn->set_status(code);
        conn->set_body(resp_body);
        conn->append_header("Content-Type", "application/json");
        return;
    }
    void reg(wsserver_t::connection_ptr& conn){
        //用户注册功能请求处理
        websocketpp::http::parser::request req=conn->get_request();
        //1.获取请求正文
        std::string body=req.get_body();
        //2. 进行反序列化
        Json::Value login_info;
        bool ret=json_util::unserialize(body,login_info);
        if(ret==false){
            DBG_LOG("反序列化失败");
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"请求正文格式错误");
        }
        if(login_info["username"].isNull() || login_info["password"].isNull()){
            DBG_LOG("用户名密码不完整");
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"请输入用户名/密码");
        }
        //3. 进⾏数据库的⽤⼾新增操作
        ret=_ut.insert(login_info);
        if(ret==false){
            DBG_LOG("向数据库插入数据失败");
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"用户名已被占用!");
        }
        // 如果成功了，则返回200
        return http_resp(conn,true,websocketpp::http::status_code::ok,"注册用户成功!");
    }

    void login(wsserver_t::connection_ptr& conn){
        //用户登录请求处理
        websocketpp::http::parser::request req=conn->get_request();
        //1.获取请求正文
        std::string body=req.get_body();
        //2.进行反序列化
        Json::Value login_info;
        bool ret=json_util::unserialize(body,login_info);
        if(ret==false){
            DBG_LOG("反序列化失败");
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"请求正文格式错误");
        }
        //3. 校验正⽂完整性，进⾏数据库的⽤⼾信息验证
        if(login_info["username"].isNull() || login_info["password"].isNull()){
            DBG_LOG("用户名密码不完整");
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"请输入用户名/密码");
        }
        ret=_ut.login(login_info);
        if(ret==false){
            DBG_LOG("用户名密码错误");
            return  http_resp(conn,false,websocketpp::http::status_code::bad_request,"用户名密码错误");
        }
        //4. 如果验证成功，给客⼾端创建session
        uint64_t uid=login_info["id"].asInt64();
        session_ptr ssp=_sm.create_session(uid,LOGIN);
        if(ssp.get()==nullptr){
            DBG_LOG("创建会话失败");
            return  http_resp(conn,false,websocketpp::http::status_code::internal_server_error,"创建会话失败");
        }
        _sm.set_session_expire_time(ssp->get_ssid(),SESSION_TIMEOUT);
        //5. 设置响应头部：Set-Cookie,将session通过cookie返回
        std::string cookie_ssid="SSID="+std::to_string(ssp->get_ssid());
        conn->append_header("Set-Cookie",cookie_ssid);
        return  http_resp(conn,true,websocketpp::http::status_code::ok,"登录成功");
    }

    bool get_cookie_val(std::string& cookie_str,const std::string& key,std::string& val){
        // Cookie: SSID=XXX; path=/;
        //1. 以 ; 作为间隔，对字符串进⾏分割，得到各个单个的cookie信息
        std::string seq="; ";
        std::vector<std::string> cookie_arr;
        string_util::split(cookie_str,seq,cookie_arr);
        for(auto& str:cookie_arr){
            //2. 对单个cookie字符串，以 = 为间隔进⾏分割，得到key和val
            std::vector<std::string> tmp_arr;
            string_util::split(str,"=",tmp_arr);
            if(tmp_arr.size()!=2){
                continue;
            }
            if(tmp_arr[0]==key){
                val=tmp_arr[1];
                return true;
            }
        }
        return false;
    }
    void info(wsserver_t::connection_ptr& conn){
        //⽤⼾信息获取功能请求的处理
        //1. 获取请求信息中的Cookie，从Cookie中获取ssid
        std::string cookie_str=conn->get_request_header("Cookie");
        if(cookie_str.empty()){
            ////如果没有cookie，返回错误：没有cookie信息，让客⼾端重新登录
            return  http_resp(conn,false,websocketpp::http::status_code::bad_request,"找不到cookie信息,请重新登录"); 
        }
        //从cookie中取出ssid
        std::string ssid_str;
        bool ret=get_cookie_val(cookie_str,"SSID",ssid_str);
        if(ret==false){
            //cookie中没有ssid，返回错误：没有ssid信息，让客⼾端重新登录
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"找不到ssid信息,请重新登录");
        }
        session_ptr ssp=_sm.get_session_by_ssid(std::stol(ssid_str));
        if(ssp.get()==nullptr){
            //没有找到session，则认为登录已经过期，需要重新登录
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"登录过期，请重新登录");
        }
        Json::Value user_info;
        ret=_ut.select_by_id(ssp->get_user(),user_info);
        if(ret==false){
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"找不到用户信息，请重新登录");
        }
        std::string body;
        json_util::serialize(user_info,body);
        conn->set_body(body);
        conn->append_header("Content-Type","application/json");
        conn->set_status(websocketpp::http::status_code::ok);
        // 刷新session的过期时间
        _sm.set_session_expire_time(ssp->get_ssid(),SESSION_TIMEOUT);
    }

    void http_handler(websocketpp::connection_hdl hdl){
        wsserver_t::connection_ptr conn=_wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req=conn->get_request();
        std::string method=req.get_method();
        std::string uri=req.get_uri();
        if(method=="POST" && uri=="/reg"){
            return reg(conn);
        }else if(method=="POST" && uri=="/login"){
            return login(conn);
        }else if(method=="GET" && uri=="/info"){
            return info(conn);
        }else{
            return file_handler(conn);
        }
    }
    void ws_resp(wsserver_t::connection_ptr& conn,Json::Value& value){
        std::string body;
        json_util::serialize(value,body);
        conn->send(body);
    }

    session_ptr get_ssp_by_cookie(wsserver_t::connection_ptr& conn){
        //获取请求信息中的Cookie，从Cookie中获取ssid
        Json::Value err_resp;
        std::string cookie_str=conn->get_request_header("Cookie");
        if(cookie_str.empty()){
            err_resp["optype"]="hall_ready";
            err_resp["result"]=false;
            err_resp["reason"]="找不到Cookie信息,请重新登录!!";
            ws_resp(conn,err_resp);
            return session_ptr();
        }
        std::string ssid_str;
        bool ret=get_cookie_val(cookie_str,"SSID",ssid_str);
        if(ret==false){
            err_resp["optype"]="hall_ready";
            err_resp["result"]=false;
            err_resp["reason"]="找不到ssid信息,请重新登录!!";
            ws_resp(conn,err_resp);
            return session_ptr();
        }
        session_ptr ssp=_sm.get_session_by_ssid(std::stol(ssid_str));
        if(ssp.get()==nullptr){
            err_resp["optype"]="hall_ready";
            err_resp["result"]=false;
            err_resp["reason"]="登录过期,请重新登录!!";
            ws_resp(conn,err_resp);
            return session_ptr();
        }
        return ssp;
    }

    void wsopen_game_hall(wsserver_t::connection_ptr& conn){
        //游戏大厅长连接建立成功
        //1. 登录验证--判断客户端是否已经登录成功
        //获取请求信息中的Cookie，从Cookie中获取ssid
        Json::Value err_resp;
        session_ptr ssp=get_ssp_by_cookie(conn);
        if(ssp.get()==nullptr){
            return;
        }
        uint64_t uid=ssp->get_user();
        //2. 判断当前用户是否重复登录
        if(_om.is_game_hall(uid) || _om.is_game_room(uid)){
            err_resp["optype"]="hall_ready";
            err_resp["result"]=false;
            err_resp["reason"]="玩家重复登录!!";
            return ws_resp(conn,err_resp);
        }
        //3. 将当前客户端以及连接加入到游戏大厅
        _om.enter_game_hall(uid,conn);
        Json::Value resp_json;
        //4. 给客户端响应游戏大厅连接建立成功
        resp_json["optype"]="hall_ready";
        resp_json["reason"]="游戏连接成功";
        resp_json["result"]=true;
        ws_resp(conn,resp_json);
        //5. 将session设置为永久存在
        _sm.set_session_expire_time(ssp->get_ssid(),SESSION_FOREVER);
    }
    void wsopen_game_room(wsserver_t::connection_ptr& conn){
        //游戏房间长连接建立成功后的处理函数
        //1. 获取当前客⼾端的session
        Json::Value resp_json;
        resp_json["optype"]="room_ready";
        session_ptr ssp=get_ssp_by_cookie(conn);
        if(ssp.get()==nullptr){
            return;
        }
        //2. 当前⽤⼾是否已经在在线⽤⼾管理的游戏房间或者游戏⼤厅中---在线⽤⼾管理
        uint64_t uid=ssp->get_user();
        if(_om.is_game_hall(uid) ||_om.is_game_room(uid)){
            resp_json["result"]=false;
            resp_json["reason"]="玩家重复登录";
            return ws_resp(conn,resp_json);
        }
        //3.  判断当前⽤⼾是否已经创建好了房间 --- 房间管理
        room_ptr rp=_rm.get_room_by_uid(uid);
        if(rp.get()==nullptr){
            resp_json["result"]=false;
            resp_json["reason"]="没有找到玩家的房间信息";
            return ws_resp(conn,resp_json);
        }
        //4. 将当前⽤⼾添加到在线⽤⼾管理的游戏房间中
        _om.enter_game_room(uid,conn);
        //5. 将session重新设置为永久存在
        _sm.set_session_expire_time(ssp->get_ssid(),SESSION_FOREVER);
        //6. 回复房间准备完毕
        resp_json["result"]=true;
        resp_json["room_id"]=(Json::Int64)rp->id();
        resp_json["uid"]=(Json::Int64)uid;
        resp_json["white_id"]=(Json::Int64)rp->get_white_user();
        resp_json["black_id"]=(Json::Int64)rp->get_black_user();
        return ws_resp(conn,resp_json);
    }
    void wsopen_handler(websocketpp::connection_hdl hdl){
        //websoket长连接建立成功之后的处理函数
        wsserver_t::connection_ptr conn=_wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req=conn->get_request();
        std::string uri=req.get_uri();
        if(uri=="/hall"){
            return wsopen_game_hall(conn);
        }else if(uri=="/room"){
            return wsopen_game_room(conn);
        }
    }
    void wsclose_game_hall(wsserver_t::connection_ptr& conn){
        //获取请求信息中的Cookie，从Cookie中获取ssid
        Json::Value err_resp;
        session_ptr ssp=get_ssp_by_cookie(conn);
        if(ssp.get()==nullptr){
            return;
        }
        //1. 将玩家从游戏大厅中移除
        _om.exit_game_hall(ssp->get_user());
       //2. 将session恢复生命周期的管理，设置定时销毁
       _sm.set_session_expire_time(ssp->get_ssid(),SESSION_TIMEOUT);
    }
    void wsclose_game_room(wsserver_t::connection_ptr& conn){
        //1.获取session信息
        session_ptr ssp=get_ssp_by_cookie(conn);
        if(ssp.get()==nullptr){
            return;
        }
        //2. 将玩家从在线⽤⼾管理中移除
        _om.exit_game_room(ssp->get_user());
        //3. 将session恢复生命周期的管理，设置定时销毁
        _sm.set_session_expire_time(ssp->get_ssid(),SESSION_TIMEOUT);
        //4. 将玩家从游戏房间中移除，房间中所有⽤⼾退出了就会销毁房间
        _rm.remove_room_user(ssp->get_user());
    }
    void wsclose_handler(websocketpp::connection_hdl hdl){
        //websoket长连接关闭之后的处理函数
        wsserver_t::connection_ptr conn=_wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req=conn->get_request();
        std::string uri=req.get_uri();
        if(uri=="/hall"){
            return wsclose_game_hall(conn);
        }else if(uri=="/room"){
            return wsclose_game_room(conn);
        }
    }
    void wsmsg_game_hall(wsserver_t::connection_ptr& conn,wsserver_t::message_ptr& msg){
        Json::Value resp_json;
        //1. ⾝份验证，当前客⼾端到底是哪个玩家
        session_ptr ssp=get_ssp_by_cookie(conn);
        if(ssp.get()==nullptr){
            return;
        }
        //2. 获取请求信息
        std::string req_body=msg->get_payload();
        Json::Value req_json;
        bool ret=json_util::unserialize(req_body,req_json);
        if(ret==false){
            resp_json["result"]=false;
            resp_json["reason"] = "请求信息解析失败";
            return ws_resp(conn,resp_json);
        }
        //3. 对于请求进⾏处理
        if(!req_json["optype"].isNull() && req_json["optype"].asString()=="match_start"){
            // 开始对战匹配：通过匹配模块，将⽤⼾添加到匹配队列中
            _mm.add(ssp->get_user());
            resp_json["optype"]="match_start";
            resp_json["result"]=true;
        }else if(!req_json["optype"].isNull() && req_json["optype"].asString()=="match_stop"){
            //停⽌对战匹配：通过匹配模块，将⽤⼾从匹配队列中移除
            _mm.del(ssp->get_user());
            resp_json["optype"]="match_stop";
            resp_json["result"]=true;
        }else{
            DBG_LOG("optype: %s",req_json["optype"].asCString());
            resp_json["optype"]="unknow";
            resp_json["reason"]="请求类型未知";
            resp_json["result"]=false;
        }
            return ws_resp(conn,resp_json);
    }
    void wsmsg_game_room(wsserver_t::connection_ptr& conn,wsserver_t::message_ptr& msg){
        Json::Value resp_json;
        //1. ⾝份验证，当前客⼾端到底是哪个玩家
        session_ptr ssp=get_ssp_by_cookie(conn);
        if(ssp.get()==nullptr){
            return;
        }
        //2. 获取房间信息
        room_ptr rp=_rm.get_room_by_uid(ssp->get_user());
        if(rp.get()==nullptr){
            resp_json["optype"]="unknow";
            resp_json["result"]=false;
            resp_json["reason"] = "没有找到玩家的房间信息";
            return ws_resp(conn,resp_json);
        }
        //3. 获取请求信息
        std::string req_body=msg->get_payload();
        Json::Value req_json;
        bool ret=json_util::unserialize(req_body,req_json);
        if(ret==false){
            resp_json["result"]=false;
            resp_json["reason"] = "请求信息解析失败";
            return ws_resp(conn,resp_json);
        }
        //4. 通过房间模块进⾏消息请求的处理
        return rp->handler_request(req_json);
    }
    void message_handler(websocketpp::connection_hdl hdl,wsserver_t::message_ptr msg){
        //websocket⻓连接通信处理
        wsserver_t::connection_ptr conn=_wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req=conn->get_request();
        std::string uri=req.get_uri();
        if(uri=="/hall"){
            return wsmsg_game_hall(conn, msg);
        }else if(uri=="/room"){
            return wsmsg_game_room(conn, msg);
        }
    }
public:
    gobang_server(const std::string host,
    const std::string& user,
    const std::string& password,
    const std::string& dbname,
    int port=3306,const std::string& wwwroot=WWWROOT)
    :_ut(host,user,password,dbname,port),_rm(&_ut,&_om),_mm(&_ut,&_om,&_rm),_sm(&_wssrv),_web_root(wwwroot){
        //设置websocketpp打印日志等级
        //all表示全部都打印
        //none表示什么日志都不打印
        _wssrv.set_access_channels(websocketpp::log::alevel::none);
        //初始化asio
        _wssrv.init_asio();
        /*设置启⽤地址重⽤*/
        _wssrv.set_reuse_addr(true);
        //注册http请求函数
        _wssrv.set_http_handler(std::bind(&gobang_server::http_handler,this,std::placeholders::_1));
        // 注册websocket请求的处理函数
        _wssrv.set_open_handler(std::bind(&gobang_server::wsopen_handler,this,std::placeholders::_1));
        _wssrv.set_close_handler(std::bind(&gobang_server::wsclose_handler,this,std::placeholders::_1));
        _wssrv.set_message_handler(std::bind(&gobang_server::message_handler,this,std::placeholders::_1,std::placeholders::_2));

    }
    void start(int port){
        _wssrv.listen(port);
        //开始接收tcp连接
        _wssrv.start_accept();
        //开始运行服务器
        _wssrv.run();
    }
};